
#include <gtest/gtest.h>
#include "ssm_state.h"

namespace {

typedef int Event;

const Event REENTER_STATE = 1;
const Event TRANSITION_TO_STATE_B = 2;
const Event OUTER_EVENT_A = 3;

using namespace SimpleStateMachineLib;

class InnerStateMachine: public StateMachine<InnerStateMachine, Event>
{
public:
    class StateA: public StateType
    {
    public:

		void onEntry()
		{
			machine()->m_pLatestEnteredState = this;
		}

        void onExit()
        {
        }

        bool handleEvent(const EventType& ev)
        {
            if(ev == REENTER_STATE)
            {
                transition(*this);
            }
            else if(ev == TRANSITION_TO_STATE_B)
            {
                transition(machine()->m_StateB);
            }
            return false;
        }
    };

    StateA m_StateA;
    StateA m_StateB;

    const StateType* m_pLatestEnteredState;

    InnerStateMachine():
    	m_pLatestEnteredState(0)
    {
        m_StateA.setStateMachine(this);
        m_StateB.setStateMachine(this);

        setInitialState(m_StateA);
    }

    void onTransition(const EventType& event,
        const StateType& fromState,
        const StateType& toState)
    {
    	m_pLatestEnteredState = &toState;
    }
};




class OuterStateMachine: public StateMachine<OuterStateMachine, Event>
{
public:

	class InnerState: public CompositeState<InnerStateMachine, StateType>
	{
	public:
		void onEntry()
		{
			machine()->m_pLatestEnteredState = this;
			BaseType::onEntry();
		}
		bool handleEvent(const EventType& ev)
		{
			if(ev == OUTER_EVENT_A)
			{
				transition(machine()->m_StateB);
			}
			bool res = BaseType::handleEvent(ev);
		}
	};

	class StateB: public StateType
	{
	};

	InnerState m_StateA;
	StateB m_StateB;

	const StateType* m_pLatestEnteredState;

	OuterStateMachine():
		m_pLatestEnteredState(0)
    {
        m_StateA.setStateMachine(this);
        m_StateB.setStateMachine(this);

        setInitialState(m_StateA);
    }
protected:
    void onTransition(const EventType& event,
        const StateType& fromState,
        const StateType& toState)
    {
    	std::cout << "OuterSM::onTransition" << std::endl;
    	m_pLatestEnteredState = &toState;
    }

};

TEST(StateMachineCompositionTest, initialTransition)
{
	OuterStateMachine sm;
	InnerStateMachine& innerSm = sm.m_StateA.getInner();
	sm.onEntry();
	EXPECT_EQ(&(sm.m_StateA), sm.m_pLatestEnteredState);
    EXPECT_EQ(&(innerSm.m_StateA), innerSm.m_pLatestEnteredState);
}


}
